﻿using Autofac;
using PlutoStudio.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Immutable;
using PlutoStudio.Dependency;

namespace PlutoStudio.Domain.UnitOfWork
{
    /// <summary>
    /// 所有 Unit Of Work 类型的基类。
    /// </summary>
    public abstract class UnitOfWorkBase : IUnitOfWork
    {
        private readonly string _id;
        private bool _isBeginCalledBefore;
        private bool _isCompleteCalledBefore;
        private bool _succeed;
        private Exception _exception;

        /// <summary>
        /// 当前工作单元的唯一ID。
        /// </summary>
        public string Id => _id;

        /// <summary>
        /// 如果外围工作单元存在，则引用外围工作单元。
        /// </summary>
        [IgnoreAutowired]
        public IUnitOfWork Outer { get; set; }

        /// <summary>
        /// 该工作单元是否已经被释放？
        /// </summary>
        public bool IsDisposed { get; private set; }

        /// <inheritdoc/>
        public UnitOfWorkOptions Options { get; private set; }

        /// <inheritdoc/>
        public IReadOnlyList<DataFilterConfiguration> Filters => _filters.ToImmutableList();

        private readonly List<DataFilterConfiguration> _filters;

        /// <summary>
        /// Gets default UOW options.
        /// </summary>
        protected IUnitOfWorkDefaultOptions DefaultOptions { get; }

        /// <summary>
        /// Gets the connection string resolver.
        /// </summary>
        protected IConnectionStringResolver ConnectionStringResolver { get; }

        protected IUnitOfWorkFilterExecuter FilterExecuter { get; }

        /// <summary>
        /// 当此工作单元成功完成时，会引发此事件。
        /// </summary>
        public event EventHandler Completed;

        /// <summary>
        /// 当此工作单元被释放时，会引发此事件。
        /// </summary>
        public event EventHandler Disposed;

        /// <summary>
        /// 当此工作单元失败时，会引发此事件。
        /// </summary>
        public event EventHandler<UnitOfWorkFailedEventArgs> Failed;

        /// <summary>
        /// 创建一个新的 <see cref="UnitOfWorkBase"/> 实例。
        /// </summary>
        protected UnitOfWorkBase(IConnectionStringResolver connectionStringResolver, IUnitOfWorkDefaultOptions defaultOptions, IUnitOfWorkFilterExecuter filterExecuter)
        {
            ConnectionStringResolver = connectionStringResolver;
            DefaultOptions = defaultOptions;
            FilterExecuter = filterExecuter;
            _id = Guid.NewGuid().ToString("N");
            _filters = defaultOptions.Filters.ToList();
        }

        /// <inheritdoc/>
        public IDisposable DisableFilter(params string[] filterNames)
        {
            //TODO: Check if filters exists?

            var disabledFilters = new List<string>();

            foreach (var filterName in filterNames)
            {
                var filterIndex = GetFilterIndex(filterName);
                if (_filters[filterIndex].IsEnabled)
                {
                    disabledFilters.Add(filterName);
                    _filters[filterIndex] = new DataFilterConfiguration(_filters[filterIndex], false);
                }
            }

            disabledFilters.ForEach(ApplyDisableFilter);

            return new DisposeAction(() => EnableFilter(disabledFilters.ToArray()));
        }

        /// <inheritdoc/>
        public IDisposable EnableFilter(params string[] filterNames)
        {
            //TODO: Check if filters exists?

            var enabledFilters = new List<string>();

            foreach (var filterName in filterNames)
            {
                var filterIndex = GetFilterIndex(filterName);
                if (!_filters[filterIndex].IsEnabled)
                {
                    enabledFilters.Add(filterName);
                    _filters[filterIndex] = new DataFilterConfiguration(_filters[filterIndex], true);
                }
            }

            enabledFilters.ForEach(ApplyEnableFilter);

            return new DisposeAction(() => DisableFilter(enabledFilters.ToArray()));
        }

        /// <inheritdoc/>
        public bool IsFilterEnabled(string filterName)
        {
            return GetFilter(filterName).IsEnabled;
        }

        /// <inheritdoc/>
        public IDisposable SetFilterParameter(string filterName, string parameterName, object value)
        {
            var filterIndex = GetFilterIndex(filterName);

            var newfilter = new DataFilterConfiguration(_filters[filterIndex]);

            //Store old value
            object oldValue = null;
            var hasOldValue = newfilter.FilterParameters.ContainsKey(parameterName);
            if (hasOldValue)
            {
                oldValue = newfilter.FilterParameters[parameterName];
            }

            newfilter.FilterParameters[parameterName] = value;

            _filters[filterIndex] = newfilter;

            ApplyFilterParameterValue(filterName, parameterName, value);

            return new DisposeAction(() =>
            {
                //Restore old value
                if (hasOldValue)
                {
                    SetFilterParameter(filterName, parameterName, oldValue);
                }
            });
        }



        /// <summary>
        /// 使用指定的<see cref="UnitOfWorkOptions"/> 选项启动工作单元。
        /// </summary>
        /// <param name="options"><see cref="UnitOfWorkOptions"/> 选项</param>
        public void Begin(UnitOfWorkOptions options)
        {
            PublicHelper.CheckArgument(options, nameof(options));

            PreventMultipleBegin();
            Options = options; //TODO: Do not set options like that, instead make a copy?
            BeginUow();
        }

        /// <summary>
        /// 完成这个工作单元。
        /// 该方法会保存所有的更改并提交事务（如果事务存在）。
        /// </summary>
        public void Complete()
        {
            PreventMultipleComplete();
            try
            {
                CompleteUow();
                _succeed = true;
                OnCompleted();
            }
            catch (Exception ex)
            {
                _exception = ex;
                throw;
            }
        }


        /// <summary>
        /// 完成这个工作单元。
        /// 该方法会保存所有的更改并提交事务（如果事务存在）。
        /// </summary>
        public async Task CompleteAsync()
        {
            PreventMultipleComplete();
            try
            {
                await CompleteUowAsync();
                _succeed = true;
                OnCompleted();
            }
            catch (Exception ex)
            {
                _exception = ex;
                throw;
            }
        }

        /// <summary>
        /// 释放当前工作单元并回收所有资源。
        /// </summary>
        public void Dispose()
        {
            if (!_isBeginCalledBefore || IsDisposed)
            {
                return;
            }
            IsDisposed = true;
            if (!_succeed)
            {
                OnFailed(_exception);
            }
            DisposeUow();
            OnDisposed();
        }

        /// <summary>
        /// 用于触发 <see cref="Failed"/> 事件。
        /// </summary>
        /// <param name="exception">导致失败的异常</param>
        protected virtual void OnFailed(Exception exception)
        {
            Failed.SafetyInvoke(this, new UnitOfWorkFailedEventArgs(exception));
        }

        /// <summary>
        /// 用于触发 <see cref="Disposed"/> 事件。
        /// </summary>
        protected virtual void OnDisposed()
        {
            Disposed.SafetyInvoke(this);
        }

        /// <summary>
        /// 用于触发 <see cref="Completed"/> 事件。
        /// </summary>
        protected virtual void OnCompleted()
        {
            Completed.SafetyInvoke(this);
        }

        /// <summary>
        /// 保存该工作单元的所有更改。
        /// 在需要时可以调用此方法来应用更改。
        /// 请注意，如果此工作单元是事务性的，则如果事务回滚，则保存的更改也将被回滚。
        /// 通常不需要显式调用SaveChanges，
        /// 因为所有更改都会自动保存在工作单元的末尾。
        /// </summary>
        public abstract void SaveChanges();

        /// <summary>
        /// 保存该工作单元的所有更改。
        /// 在需要时可以调用此方法来应用更改。
        /// 请注意，如果此工作单元是事务性的，则如果事务回滚，则保存的更改也将被回滚。
        /// 通常不需要显式调用SaveChanges，
        /// 因为所有更改都会自动保存在工作单元的末尾。
        /// </summary>
        public abstract Task SaveChangesAsync();

        /// <summary>
        /// 可以通过派生类来实现释放工作单元并回收资源。
        /// </summary>
        protected abstract void DisposeUow();

        /// <summary>
        /// 可以通过派生类来实现启动工作单元。
        /// </summary>
        protected abstract void BeginUow();

        /// <summary>
        /// 可以通过派生类来实现完成工作单元。
        /// </summary>
        protected abstract void CompleteUow();

        /// <summary>
        /// 可以通过派生类来实现完成工作单元。
        /// </summary>
        protected abstract Task CompleteUowAsync();


        private void PreventMultipleBegin()
        {
            if (_isBeginCalledBefore)
            {
                throw new ComponentException("This unit of work has started before. Can not call Start method more than once.");
            }
            _isBeginCalledBefore = true;
        }

        private void PreventMultipleComplete()
        {
            if (_isCompleteCalledBefore)
            {
                throw new ComponentException("Complete is called before!");
            }

            _isCompleteCalledBefore = true;
        }

        private void SetFilters(List<DataFilterConfiguration> filterOverrides)
        {
            for (var i = 0; i < _filters.Count; i++)
            {
                var filterOverride = filterOverrides.FirstOrDefault(f => f.FilterName == _filters[i].FilterName);
                if (filterOverride != null)
                {
                    _filters[i] = filterOverride;
                }
            }
        }

        private void ChangeFilterIsEnabledIfNotOverrided(List<DataFilterConfiguration> filterOverrides, string filterName, bool isEnabled)
        {
            if (filterOverrides.Any(f => f.FilterName == filterName))
            {
                return;
            }

            var index = _filters.FindIndex(f => f.FilterName == filterName);
            if (index < 0)
            {
                return;
            }

            if (_filters[index].IsEnabled == isEnabled)
            {
                return;
            }

            _filters[index] = new DataFilterConfiguration(filterName, isEnabled);
        }

        private DataFilterConfiguration GetFilter(string filterName)
        {
            var filter = _filters.FirstOrDefault(f => f.FilterName == filterName);
            if (filter == null)
            {
                throw new FrameworkException("Unknown filter name: " + filterName + ". Be sure this filter is registered before.");
            }

            return filter;
        }

        private int GetFilterIndex(string filterName)
        {
            var filterIndex = _filters.FindIndex(f => f.FilterName == filterName);
            if (filterIndex < 0)
            {
                throw new FrameworkException("Unknown filter name: " + filterName + ". Be sure this filter is registered before.");
            }

            return filterIndex;
        }

        protected virtual void ApplyDisableFilter(string filterName)
        {
            FilterExecuter.ApplyDisableFilter(this, filterName);
        }

        protected virtual void ApplyEnableFilter(string filterName)
        {
            FilterExecuter.ApplyEnableFilter(this, filterName);
        }

        protected virtual void ApplyFilterParameterValue(string filterName, string parameterName, object value)
        {
            FilterExecuter.ApplyFilterParameterValue(this, filterName, parameterName, value);
        }


        protected virtual string ResolveConnectionString(ConnectionStringResolveArgs args)
        {
            return ConnectionStringResolver.GetNameOrConnectionString(args);
        }

    }
}
